﻿/*
 * Copyright © 2020, Wuxi Safover Intelligent Technology Co., Ltd. All rights reserved.
 * 
 * 无锡安永智能科技有限公司版权所有，未经许可，不得用于商业用途
 * 
 * Visit: https://www.safover.com/ 
 */

using SciChart.Charting.Model.ChartSeries;
using SciChart.Charting.Model.DataSeries;
using SafoverWpfFw;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using Safover.LidarCollector.Core;
using Safover.LidarCollector.Managers;
using static Safover.LidarCollector.ViewModel.DI;
using Safover.LidarCollector.Config;
using System.Diagnostics;
using Safover.Lidar.DataContract;

namespace Safover.LidarCollector.ViewModel
{
    public class CollectionPageViewModel : BaseViewModel, ILidarDataListenner
    {
        public ChartControlViewModel ChartControlViewModel { get; } = new ChartControlViewModel();

        public bool ShowDeviceControlPanel { get; set; }

        public ICommand LogoutCommand { get; }

        public ICommand ConfigCommand { get; }

        public ICommand StartCollectionCommand { get; }

        public ICommand StopCollectionCommand { get; }

        public ICommand AdjustCommand { get; }

        public ICommand ShowDeviceControlPanelCommand { get; }

        public ICommand TrySendCommand { get; set; }

        /// <summary>
        /// 当前周期性扫描模式
        /// </summary>
        public LidarWorkMode CurrentLidarWorkMode { get; set; }

        /// <summary>
        /// 系统全局配置的扫描模式
        /// </summary>
        public LidarWorkMode ConfiguredLidarWorkMode => RuntimeConfigManager.Instance.LidarConfig.LidarWorkMode;


        #region Single collection update information

        public LidarWorkMode SingleLidarWorkMode { get; set; }
        
        public float SinglePercentageCompletion { get; set; }

        public string Message { get; set; }

        /// <summary>
        /// 是否在采集状态中
        /// </summary>
        public bool IsCollecting { get; set; }

        public bool IsSingleCollecting { get; set; }

        public TimeSpan CountDownSecondsForNextSingleConnection { get; set; }

        public bool IsPreiodicalCollecting { get; set; }

        public TimeSpan CountDownSecondsForNextPeriodicCollection { get; set; }

        #endregion

        public ObservableCollection<IRenderableSeriesViewModel> RenderableSeries { get; set; }

        public XyDataSeries<double, double> Channel0 { get; } = new XyDataSeries<double, double>();
        public XyDataSeries<double, double> Channel1 { get; } = new XyDataSeries<double, double>();
        public XyDataSeries<double, double> Channel2 { get; } = new XyDataSeries<double, double>();
        public XyDataSeries<double, double> Channel3 { get; } = new XyDataSeries<double, double>();

        DataCollector DataCollector => DataCollector.Instance;

        readonly List<double> values0 = new List<double>();
        readonly List<double> values1 = new List<double>();
        readonly List<double> values2 = new List<double>();
        readonly List<double> values3 = new List<double>();

        bool showChannel0 = true;
        public bool ShowChannel0
        {
            get { return showChannel0; }
            set
            {
                showChannel0 = value;
                if (showChannel0)
                {
                    ShowChannel(Channel0, values0.ToArray());
                }
                else
                {
                    Channel0.Clear();
                }
            }
        }
        bool showChannel1 = true;
        public bool ShowChannel1
        {
            get { return showChannel1; }
            set
            {
                showChannel1 = value;
                if (showChannel1)
                {
                    ShowChannel(Channel1, values1.ToArray());
                }
                else
                {
                    Channel1.Clear();
                }
            }
        }

        bool showChannel2 = true;
        public bool ShowChannel2
        {
            get { return showChannel2; }
            set
            {
                showChannel2 = value;
                if (showChannel2)
                {
                    ShowChannel(Channel2, values2.ToArray());
                }
                else
                {
                    Channel2.Clear();
                }
            }
        }

        bool showChannel3 = true;
        public bool ShowChannel3
        {
            get { return showChannel3; }
            set
            {
                showChannel3 = value;
                if (showChannel3)
                {
                    ShowChannel(Channel3, values3.ToArray());
                }
                else
                {
                    Channel3.Clear();
                }
            }
        }

        private void ShowChannel(XyDataSeries<double, double> channel, double[] values)
        {
            if (values == null || values.Length <= 0) return;

            channel.Clear();

            var modified = ModifyData(values);

            for (int x = 0; x < modified.Length; x += 10)
            {
                var y = modified[x];

                channel.Append(x * 0.0075, y);
            }
        }

        private double[] ModifyData(double[] originalValues)
        {
            const double divisor = 7.5;
            const int offset = 32768;
            const int max = ushort.MaxValue;

            Debug.Assert(originalValues != null && originalValues.Length > 0);
         
            // 累加次数
            int addNumber = RuntimeConfigManager.Instance.AcquisitionCardConfig.CumulativePulses;

            var values = new double[originalValues.Length];
            for (int i = 0; i < originalValues.Length; i++)
            {
                var originalValue = originalValues[i];
                var v = 4000 * (originalValue / addNumber - offset) / max;
                values[i] = v;
            }

            var useSubBaseLine = RuntimeConfigManager.Instance.OthersConfig.UseSubBaseLine;
            var useEmptyMining = RuntimeConfigManager.Instance.OthersConfig.UseEmptyMining;

            if (useSubBaseLine || useEmptyMining)
            {
                var subBaseLinedData = new List<double>();

                if (useSubBaseLine)
                {
                    var startIndex = Convert.ToInt32(RuntimeConfigManager.Instance.OthersConfig.SubBaseLineStart / divisor);
                    var endIndex = Convert.ToInt32(RuntimeConfigManager.Instance.OthersConfig.SubBaseLineEnd / divisor);

                    if (startIndex >= endIndex)
                    {
                        throw new SafoverConfigurationErrorException($"SubBaseLineStart=[{startIndex} x {divisor}] is larger than or equal to SubBaseLineEnd=[{endIndex} x {divisor}].");
                    }

                    if (startIndex >= values.Length)
                    {
                        throw new SafoverConfigurationErrorException($"SubBaseLineStart=[{startIndex} x {divisor}], index out of the range of received value, length=[{values.Length}].");
                    }

                    if (endIndex >= values.Length)
                    {
                        throw new SafoverConfigurationErrorException($"SubBaseLineEnd=[{endIndex} x {divisor}], index out of the range of received value, length=[{values.Length}].");
                    }

                    //values.Average()
                    var total = 0d;
                    for (int i = startIndex; i < endIndex; i++)
                    {
                        total += values[i];
                    }

                    var average = total / (endIndex - startIndex);

                    foreach (var v in values)
                    {
                        subBaseLinedData.Add(v - average);
                    }
                }

                var emptiedMiningData = new List<double>();

                if (useEmptyMining)
                {
                    var emptyNumber = Convert.ToInt32(RuntimeConfigManager.Instance.OthersConfig.EmptyMiningDistance / divisor);
                    if (!useSubBaseLine)
                    {
                        for (int i = emptyNumber; i < values.Length; i++)
                        {
                            emptiedMiningData.Add(values[i]);
                        }

                        for (int i = values.Length - emptyNumber; i < values.Length; i++)
                        {
                            emptiedMiningData.Add(values[i]);
                        }

                        return emptiedMiningData.ToArray();
                    }
                    else
                    {
                        for (int i = emptyNumber; i < subBaseLinedData.Count; i++)
                        {
                            emptiedMiningData.Add(subBaseLinedData[i]);
                        }

                        for (int i = values.Length - emptyNumber; i < subBaseLinedData.Count; i++)
                        {
                            emptiedMiningData.Add(subBaseLinedData[i]);
                        }
                    }
                }

                return subBaseLinedData.ToArray();
            }
            else
            {
                return values;
            }
        }

        public CollectionPageViewModel()
        {
            LogoutCommand = new RelayCommand(() => ViewModelApplication.HandleLogout());

            ConfigCommand = new RelayCommand(() => ViewModelApplication.GoToPage(ApplicationPage.Config));

            AdjustCommand = new RelayCommand(Adjust);

            StartCollectionCommand = new RelayCommand(StartCollection, (object o) =>
            {
                return !IsCollecting;
            });

            StopCollectionCommand = new RelayCommand(StopCollection, (object o) =>
            {
                return IsCollecting;
            });

            ShowDeviceControlPanelCommand = new RelayCommand(() => ShowDeviceControlPanel ^= true);

            TrySendCommand = new RelayCommand(Try);

            try
            {
                DeviceManager.DeviceFactoryInstance.AcquisitionCard.Subscribe(this);
            }
            catch { }

            DataCollector.GlobalCollectionWorkingStatusChangedEvent += OnGlobalCollectionWorkingStatusChanged;

            DataCollector.PeriodicCollectionWorkingStatusChangedEvent += OnPeriodicCollectionWorkingStatusChanged;

            DataCollector.MultiStepScanStatusChangedEvent += OnMultiStepScanStatusChanged;
        }

        /// <summary>
        /// Multi 工作模式下，数据是否正在实际采集中
        /// </summary>
        public bool IsMultiStepCollecting { get; set; }
        /// <summary>
        /// Multi工作模式下，单次周期结束后，距离下个周期开始前的等待倒计时（s）
        /// </summary>
        public TimeSpan CountDownSecondsForNextMultiStepCollection { get; set; }

        /// <summary>
        /// Multi工作模式下，正在进行的单次采集距离结束的倒计时（s）
        /// </summary>
        public TimeSpan CountDownSecondsForCurrentMultiStepCollection { get; set; }

        private void OnMultiStepScanStatusChanged(MultiStepScanStatusChangedEvent e)
        {
            IsMultiStepCollecting = e.IsMultiStepCollecting;

            CountDownSecondsForNextMultiStepCollection = TimeSpan.FromSeconds(e.CountDownSecondsForNextMultiStepCollection);

            CountDownSecondsForCurrentMultiStepCollection = TimeSpan.FromSeconds(e.CountDownSecondsForCurrentMultiStepCollection);
        }

        private void OnSingleCollectionWorkingStatusChanged(SingleCollectionWorkingStatusChangedEventArgs e)
        {
            IsSingleCollecting = e.IsSingleCollecting;

            SingleLidarWorkMode = e.SingleLidarWorkMode;

            SinglePercentageCompletion = e.PercentageCompletion;
            CountDownSecondsForNextSingleConnection = TimeSpan.FromSeconds(e.CountDownSecondsForNextFixedCollection);
            CountDownSecondsForNextPeriodicCollection = TimeSpan.FromSeconds(e.CountDownSecondsForNextPeriodicCollection);

            Message = e.Message;

            //PrintStatus("OnSingleCollectionWorkingStatusChanged");
        }

        private void PrintStatus(string e)
        {
            Debug.WriteLine($"{e}: 工作中=[{IsCollecting}], 周期采集中=[{IsPreiodicalCollecting}], 单次采集中=[{IsSingleCollecting}]");
        }

        private void OnPeriodicCollectionWorkingStatusChanged(PeriodicCollectionWorkingStatusChangedEventArgs e)
        {
            IsPreiodicalCollecting = e.IsPeriodicalCollecting;
            CurrentLidarWorkMode = e.PeriodicaLidarWorkMode;

            PrintStatus("OnPeriodicCollectionWorkingStatusChanged");
        }

        private void OnGlobalCollectionWorkingStatusChanged(GlobalCollectionWorkingStatusChangedEventArgs e)
        {
            IsCollecting = e.IsCollecting;
            //ConfiguredLidarWorkMode = e.LidarWorkMode;

            PrintStatus("OnGlobalCollectionWorkingStatusChanged");
        }

        private void Try()
        {
            return;
        }
     
        private void Adjust()
        { }

        private void StopCollection()
        {
            DataCollector.StopWorking();

            DataCollector.SingleCollectionWorkingStatusChangedEvent -= OnSingleCollectionWorkingStatusChanged;
        }

        private void StartCollection()
        {
            DataCollector.SingleCollectionWorkingStatusChangedEvent += OnSingleCollectionWorkingStatusChanged;

            DataCollector.StartWorking();
        }

        void ILidarDataListenner.NotifyLidarData(LidarDataCollectedEventArgs e)
        {
            Application.Current.Dispatcher.Invoke(() => ShowLidarData(e));
        }

        private void ShowLidarData(LidarDataCollectedEventArgs e)
        {
            XyDataSeries<double, double> channel;
            List<double> values;
            switch (e.ChannelNumber)
            {
                case 0:
                    {
                        channel = Channel0;
                        values = values0;
                    }
                    break;
                case 1:
                    {
                        channel = Channel1;
                        values = values1;
                    }
                    break;
                case 2:
                    {
                        channel = Channel2;
                        values = values2;
                    }
                    break;
                case 3:
                    {
                        channel = Channel3;
                        values = values3;
                    }
                    break;
                default:
                    throw new AcquisitionCardMalfunctionException($"Undefined data channel received, number=[{e.ChannelNumber}].");
            }

            values.Clear();
            values.AddRange(e.Values);

            switch (e.ChannelNumber)
            {
                case 0:
                    if (ShowChannel0)
                    {
                        ShowChannel(channel, values.ToArray());
                    }
                    break;
                case 1:
                    if (ShowChannel1)
                    {
                        ShowChannel(channel, values.ToArray());
                    }
                    break;
                case 2:
                    if (ShowChannel2)
                    {
                        ShowChannel(channel, values.ToArray());
                    }
                    break;
                case 3:
                    if (ShowChannel3)
                    {
                        ShowChannel(channel, values.ToArray());
                    }
                    break;
                default:
                    throw new AcquisitionCardMalfunctionException($"Undefined data channel received, number=[{e.ChannelNumber}].");
            }
        }
    }
}
